/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.zhiqim.kernel.constants.SignConstants;
import org.zhiqim.kernel.constants.ZhiqimConstants;
import org.zhiqim.kernel.model.Filters;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Lists;
import org.zhiqim.kernel.util.Systems;
import org.zhiqim.kernel.util.Validates;

/**
 * 知启蒙工程类路径，默认取当前目录下的lib目录和配置boot.libext的目录数组
 * 
 * @version v1.0.0 @author zouzhigang 2014-2-27 新建与整理
 */
public final class ZhiqimClassLoader extends URLClassLoader implements ZhiqimConstants, SignConstants
{
    private final boolean development;
    private final List<String> classpath;
    
    public ZhiqimClassLoader(ClassLoader parent, boolean development)
    {
        super(new URL[0], parent);
        
        this.development = development;
        this.classpath = new ArrayList<>();
    }
    
    /** 开启 */
    boolean open() throws IOException
    {
        if (development)
        {//开发模式下由ZhiqimStudio或Eclipse等IDE加载lib
            return loadClassAliasName();
        }
        
        HashSet<String> alreadys = new HashSet<>();
        
        //1.加入lib目录
        File libFolder = new File(Z_LIB_FOLDER);
        addJarFolder(Z_LIB_FOLDER, libFolder);
        alreadys.add(libFolder.getCanonicalPath());
        
        //2.加入配置的boot.libext目录下*.jar和*.zip
        String libPaths = Z.conf().getString(Z_BOOT, Z_ITEM_LIBEXT);
        List<String> libPathList = Lists.toStringList(libPaths);
        for (String libPath : libPathList)
        {
            File libDir = new File(libPath);
            if (!Files.isDirectory(libDir))
                continue;
            
            String canonicalPath = libDir.getCanonicalPath();
            if (alreadys.contains(canonicalPath))
                continue;//排重
            
            addJarFolder(libPath, libDir);
            alreadys.add(canonicalPath);
        }

        //3.设置ClassLoader
        int size = classpath.size();
        for (int i=0;i<size;i++) 
        {
            addURL(new File(classpath.get(i)).toURI().toURL());
        }
        
        //4.设置classpath&加载别名
        Systems.setClassPath(toClasspath());
        return loadClassAliasName();
    }
    
    /** 加载目录下jar,zip到列表 */
    private void addJarFolder(String libPath, File libDir) throws IOException
    {
        //2.1把目录下*.jar和*.zip加入到classpath中
        File[] files = libDir.listFiles(Filters.JAR);
        for (int i=0;i<files.length;i++)
        {
            String filePath = files[i].getCanonicalPath();
            if (classpath.contains(filePath))
                continue;
            
            classpath.add(filePath);
        }
    
        //2.2把目录加到虚拟机动态库目录，方便JNI调用的.dll,.so等库
        Systems.addLibraryPath(libPath);
    }
    
    /** 加载类短别称，在全类路径下进行include/exclude处理 */
    private boolean loadClassAliasName()
    {
        List<String> pathList = Lists.toList(Systems.getClassPaths());
        String include = Z.conf().getString(Z_BOOT, Z_ITEM_INCLUDE);
        String exclude = Z.conf().getString(Z_BOOT, Z_ITEM_EXCLUDE);
        
        if (Validates.isNotEmptyBlank(include))
        {
            List<String> patternList = Lists.toStringList(Files.toLinuxPath(include));
            first:for (Iterator<String> it=pathList.iterator();it.hasNext();)
            {
                String path = Files.toLinuxPath(it.next());
                for (String pattern : patternList)
                {
                    if (Validates.isMatch(path, pattern))
                    {//匹配一个即可
                        continue first;
                    }
                }
                
                //未包含的删除
                it.remove();
            }
        }
        
        if (Validates.isNotEmptyBlank(exclude))
        {
            List<String> patternList = Lists.toStringList(Files.toLinuxPath(exclude));
            first:for (Iterator<String> it=pathList.iterator();it.hasNext();)
            {
                String path = Files.toLinuxPath(it.next());
                for (String pattern : patternList)
                {
                    if (Validates.isMatch(path, pattern))
                    {//匹配一个则排删
                        it.remove();
                        continue first;
                    }
                }
            }
        }
        
        //最后去加载
        return Zhiqim.loadClassAliasName(pathList);
    }
    
    /** 生成classpath */
    public String toClasspath()
    {
        int size = classpath.size();
        if (size == 0)
            return _EMPTY_;
        
        StringBuilder strb = new StringBuilder();
        strb.append(classpath.get(0));
        for (int i=1;i<size;i++) 
        {
            strb.append(File.pathSeparatorChar).append(classpath.get(i));
        }
        
        return strb.toString();
    }
    
    @Override
    public String toString()
    {
        return Z_CLASS_LOADER;
    }
}
